#!/bin/bash

# Created by argbash-init v2.8.1
# Rearrange the order of options below according to what you would like to see in the help message.
# ARG_OPTIONAL_SINGLE([oval],[o],[OVAL version],[auto])
# ARG_OPTIONAL_SINGLE([builder],[b],[Builder engine],[auto])
# ARG_OPTIONAL_SINGLE([jobs],[j],[Count of simultaneous jobs],[auto])
# ARG_OPTIONAL_BOOLEAN([debug],[],[Make a debug build with draft profiles],[off])
# ARG_OPTIONAL_BOOLEAN([derivatives],[],[Also build derivatives of products if applicable],[off])
# ARG_OPTIONAL_BOOLEAN([ansible-playbooks],[],[Build Ansible Playbooks for every profile],[on])
# ARG_OPTIONAL_BOOLEAN([bash-scripts],[],[Build Bash remediation scripts for every profile],[on])
# ARG_OPTIONAL_BOOLEAN([datastream-only],[d],[Build the datastream only. Do not build any of the guides, tables, etc],[off])
# ARG_OPTIONAL_BOOLEAN([profiling],[p],[Use ninja and call the build_profiler.sh util],[off])
# ARG_USE_ENV([ADDITIONAL_CMAKE_OPTIONS],[],[Whitespace-separated string of arguments to pass to CMake])
# ARG_POSITIONAL_INF([product],[Products to build, ALL means all products],[0],[ALL])
# ARG_DEFAULTS_POS([])
# ARG_TYPE_GROUP_SET([oval_ver],[VERSION],[oval],[5.11,auto])
# ARG_TYPE_GROUP_SET([builder_type],[BUILDER],[builder],[make,ninja,auto])
# ARG_HELP([Wipes out contents of the 'build' directory and builds only and only the given products.])
# ARGBASH_GO()
# needed because of Argbash --> m4_ignore([
### START OF CODE GENERATED BY Argbash v2.10.0 one line above ###
# Argbash is a bash code generator used to get arguments parsing right.
# Argbash is FREE SOFTWARE, see https://argbash.io for more info

# Setting environmental variables


die()
{
	local _ret="${2:-1}"
	test "${_PRINT_HELP:-no}" = yes && print_help >&2
	echo "$1" >&2
	exit "${_ret}"
}

# validators

oval_ver()
{
	local _allowed=("5.11" "auto") _seeking="$1"
	for element in "${_allowed[@]}"
	do
		test "$element" = "$_seeking" && echo "$element" && return 0
	done
	die "Value '$_seeking' (of argument '$2') doesn't match the list of allowed values: '5.11' and 'auto'" 4
}


builder_type()
{
	local _allowed=("make" "ninja" "auto") _seeking="$1"
	for element in "${_allowed[@]}"
	do
		test "$element" = "$_seeking" && echo "$element" && return 0
	done
	die "Value '$_seeking' (of argument '$2') doesn't match the list of allowed values: 'make', 'ninja' and 'auto'" 4
}


begins_with_short_option()
{
	local first_option all_short_options='objdph'
	first_option="${1:0:1}"
	test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}

# THE DEFAULTS INITIALIZATION - POSITIONALS
_positionals=()
_arg_product=("ALL")
# THE DEFAULTS INITIALIZATION - OPTIONALS
_arg_oval="auto"
_arg_builder="auto"
_arg_jobs="auto"
_arg_debug="off"
_arg_derivatives="off"
_arg_ansible_playbooks="on"
_arg_bash_scripts="on"
_arg_datastream_only="off"
_arg_profiling="off"


print_help()
{
	printf '%s\n' "Wipes out contents of the 'build' directory and builds only and only the given products."
	printf 'Usage: %s [-o|--oval <VERSION>] [-b|--builder <BUILDER>] [-j|--jobs <arg>] [--(no-)debug] [--(no-)derivatives] [--(no-)ansible-playbooks] [--(no-)bash-scripts] [-d|--(no-)datastream-only] [-p|--(no-)profiling] [-h|--help] [<product-1>] ... [<product-n>] ...\n' "$0"
	printf '\t%s\n' "<product>: Products to build, ALL means all products (defaults for <product>: 'ALL')"
	printf '\t%s\n' "-o, --oval: OVAL version. Can be one of: '5.11' or 'auto' (default: 'auto')"
	printf '\t%s\n' "-b, --builder: Builder engine. Can be one of: 'make', 'ninja' and 'auto' (default: 'auto')"
	printf '\t%s\n' "-j, --jobs: Count of simultaneous jobs (default: 'auto')"
	printf '\t%s\n' "--debug, --no-debug: Make a debug build with draft profiles (off by default)"
	printf '\t%s\n' "--derivatives, --no-derivatives: Also build derivatives of products if applicable (off by default)"
	printf '\t%s\n' "--ansible-playbooks, --no-ansible-playbooks: Build Ansible Playbooks for every profile (on by default)"
	printf '\t%s\n' "--bash-scripts, --no-bash-scripts: Build Bash remediation scripts for every profile (on by default)"
	printf '\t%s\n' "-d, --datastream-only, --no-datastream-only: Build the datastream only. Do not build any of the guides, tables, etc (off by default)"
	printf '\t%s\n' "-p, --profiling, --no-profiling: Use ninja and call the build_profiler.sh util (off by default)"
	printf '\t%s\n' "-h, --help: Prints help"
	printf '\nEnvironment variables that are supported:\n'
	printf '\t%s\n' "ADDITIONAL_CMAKE_OPTIONS: Whitespace-separated string of arguments to pass to CMake."

}


parse_commandline()
{
	_positionals_count=0
	while test $# -gt 0
	do
		_key="$1"
		case "$_key" in
			-o|--oval)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_oval="$(oval_ver "$2" "oval")" || exit 1
				shift
				;;
			--oval=*)
				_arg_oval="$(oval_ver "${_key##--oval=}" "oval")" || exit 1
				;;
			-o*)
				_arg_oval="$(oval_ver "${_key##-o}" "oval")" || exit 1
				;;
			-b|--builder)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_builder="$(builder_type "$2" "builder")" || exit 1
				shift
				;;
			--builder=*)
				_arg_builder="$(builder_type "${_key##--builder=}" "builder")" || exit 1
				;;
			-b*)
				_arg_builder="$(builder_type "${_key##-b}" "builder")" || exit 1
				;;
			-j|--jobs)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_jobs="$2"
				shift
				;;
			--jobs=*)
				_arg_jobs="${_key##--jobs=}"
				;;
			-j*)
				_arg_jobs="${_key##-j}"
				;;
			--no-debug|--debug)
				_arg_debug="on"
				test "${1:0:5}" = "--no-" && _arg_debug="off"
				;;
			--no-derivatives|--derivatives)
				_arg_derivatives="on"
				test "${1:0:5}" = "--no-" && _arg_derivatives="off"
				;;
			--no-ansible-playbooks|--ansible-playbooks)
				_arg_ansible_playbooks="on"
				test "${1:0:5}" = "--no-" && _arg_ansible_playbooks="off"
				;;
			--no-bash-scripts|--bash-scripts)
				_arg_bash_scripts="on"
				test "${1:0:5}" = "--no-" && _arg_bash_scripts="off"
				;;
			-d|--no-datastream-only|--datastream-only)
				_arg_datastream_only="on"
				test "${1:0:5}" = "--no-" && _arg_datastream_only="off"
				;;
			-d*)
				_arg_datastream_only="on"
				_next="${_key##-d}"
				if test -n "$_next" -a "$_next" != "$_key"
				then
					{ begins_with_short_option "$_next" && shift && set -- "-d" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
				fi
				;;
			-p|--no-profiling|--profiling)
				_arg_profiling="on"
				test "${1:0:5}" = "--no-" && _arg_profiling="off"
				;;
			-p*)
				_arg_profiling="on"
				_next="${_key##-p}"
				if test -n "$_next" -a "$_next" != "$_key"
				then
					{ begins_with_short_option "$_next" && shift && set -- "-p" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
				fi
				;;
			-h|--help)
				print_help
				exit 0
				;;
			-h*)
				print_help
				exit 0
				;;
			*)
				_last_positional="$1"
				_positionals+=("$_last_positional")
				_positionals_count=$((_positionals_count + 1))
				;;
		esac
		shift
	done
}


assign_positional_args()
{
	local _positional_name _shift_for=$1
	_positional_names=""
	_our_args=$((${#_positionals[@]} - 0))
	for ((ii = 0; ii < _our_args; ii++))
	do
		_positional_names="$_positional_names _arg_product[$((ii + 0))]"
	done

	shift "$_shift_for"
	for _positional_name in ${_positional_names}
	do
		test $# -gt 0 || break
		eval "$_positional_name=\${1}" || die "Error during argument parsing, possibly an Argbash bug." 1
		shift
	done
}

parse_commandline "$@"
assign_positional_args 1 "${_positionals[@]}"

# OTHER STUFF GENERATED BY Argbash
# Validation of values



### END OF CODE GENERATED BY Argbash (sortof) ### ])
# [ <-- needed because of Argbash


to_uppercase() {
	printf '%s' "$1" | tr '[:lower:]' '[:upper:]'
}

to_lowercase() {
	printf '%s' "$1" | tr '[:upper:]' '[:lower:]'
}

opt_product_in() {
	switch="-DSSG_PRODUCT_$(to_uppercase "$1")=ON"
	echo "$switch"
}

is_product() {
	local candidate="$1"
	for cmake_product in "${all_cmake_products[@]}"; do
		if test "$(to_uppercase "$candidate")" = "$cmake_product"; then
			return 0
		fi
	done

	return 1
}

build_with_ninja() {
	cmake_generator="Ninja"
	build_command="ninja"
}

build_with_make() {
	cmake_generator="Unix Makefiles"
	build_command="make"
}

autodetect_builder() {
	if command -v ninja &>/dev/null ; then
		build_with_ninja
	else
		build_with_make
	fi
}

handle_wrong_products() {
	all_cmake_lowercase=()
	for p in "${all_cmake_products[@]}"; do
		all_cmake_lowercase+=("$(to_lowercase "$p")")
	done
	possible_products=$'\n'"$(printf ' * %s\n' "${all_cmake_lowercase[@]}")"

	printf '%s is not a valid product, choose one or more product names from the list: %s\n' "$1" "$possible_products"
	exit 1
}

set_no_derivatives_options() {
	test "$_arg_derivatives" = on && return
	if grep -q 'rhel' <<< "${_arg_product[*]}"; then
		CMAKE_OPTIONS+=("-DSSG_CENTOS_DERIVATIVES_ENABLED:BOOL=OFF")
	fi
	if grep -q 'rhel7' <<< "${_arg_product[*]}"; then
		CMAKE_OPTIONS+=("-DSSG_SCIENTIFIC_LINUX_DERIVATIVES_ENABLED:BOOL=OFF")
	fi
}

set_explict_build_targets() {
	if test "$_arg_datastream_only" = on ; then
		for chosen_product in "${_arg_product[@]}"; do
			EXPLICIT_BUILD_TARGETS+=("generate-ssg-$(to_lowercase "$chosen_product")-ds.xml")
		done
	fi
}

# Get this using
# grep 'option(SSG_PRODUCT' CMakeLists.txt  | sed -e 's/option(SSG_PRODUCT_\(\w\+\).*/\1/'
all_cmake_products=(
	ALINUX2
	ALINUX3
	ANOLIS8
	CHROMIUM
	DEBIAN10
	DEBIAN11
	EXAMPLE
	EKS
	FEDORA
	FIREFOX
	OCP4
	RHCOS4
	OL7
	OL8
	OL9
	OPENSUSE
	RHEL7
	RHEL8
	RHEL9
	RHV4
	SLE12
	SLE15
	UBUNTU1604
	UBUNTU1804
	UBUNTU2004
	UBUNTU2204
	UOS20
	MACOS1015
)

DEFAULT_OVAL_MAJOR_VERSION=5
DEFAULT_OVAL_MINOR_VERSION=11

build_type_option="-DCMAKE_BUILD_TYPE=Release"

cores=$(nproc 2>/dev/null) || cores=1


if test "$_arg_oval" = auto; then
	oval_major_version_option="-DSSG_TARGET_OVAL_MAJOR_VERSION:STRING=$DEFAULT_OVAL_MAJOR_VERSION"
	oval_minor_version_option="-DSSG_TARGET_OVAL_MINOR_VERSION:STRING=$DEFAULT_OVAL_MINOR_VERSION"
else
	oval_major_version_option="-DSSG_TARGET_OVAL_MAJOR_VERSION:STRING=$(cut -f 1 -d . <<< "$_arg_oval")"
	oval_minor_version_option="-DSSG_TARGET_OVAL_MINOR_VERSION:STRING=$(cut -f 1 -d . --complement <<< "$_arg_oval")"
fi

test "$_arg_debug" = on && build_type_option="-DCMAKE_BUILD_TYPE=Debug"

jobs="$_arg_jobs"
test "$jobs" = auto && jobs=$cores

if test "$_arg_profiling" = on; then
	build_with_ninja
	jobs=1
elif test "$_arg_builder" = ninja; then
	build_with_ninja
elif test "$_arg_builder" = make; then
	build_with_make
else
	autodetect_builder
fi

test "${_arg_product[0]}" = ALL && _arg_product=("${all_cmake_products[@]}")

cmake_enable_args=()
for chosen_product in "${_arg_product[@]}"; do
	if is_product "$chosen_product"; then
		cmake_enable_args+=("$(opt_product_in "$chosen_product")")
	else
		handle_wrong_products "$chosen_product"
	fi
done

CMAKE_OPTIONS=(${ADDITIONAL_CMAKE_OPTIONS} "${build_type_option}" "${oval_major_version_option}" "${oval_minor_version_option}" '-DSSG_PRODUCT_DEFAULT=OFF' "${cmake_enable_args[@]}" -G "$cmake_generator")
set_no_derivatives_options
if [ "$_arg_ansible_playbooks" = off ] ; then
	CMAKE_OPTIONS+=("-DSSG_ANSIBLE_PLAYBOOKS_ENABLED:BOOL=OFF")
fi
if [ "$_arg_bash_scripts" = off ] ; then
	CMAKE_OPTIONS+=("-DSSG_BASH_SCRIPTS_ENABLED:BOOL=OFF")
fi
EXPLICIT_BUILD_TARGETS=()
set_explict_build_targets

set -e
rm -rf build/*
cd build
cmake .. "${CMAKE_OPTIONS[@]}"
$build_command "-j${jobs}" "${EXPLICIT_BUILD_TARGETS[@]}"

if test "$_arg_profiling" = on; then
	# remove cycles in ninja logs
	ninja -t recompact
	# go back to content dir
	cd ..
	utils/build_profiler.sh `echo "${_arg_product[@]}" | sed 's/ /_/g'`
fi
# ] <-- needed because of Argbash
